Skip to content

Fix macOS filesystem security: use /etc/paths.d/TinyTeX when /usr/local/bin is not writable#489

Merged
yihui merged 16 commits intomainfrom
copilot/fix-filesystem-security-issues
Mar 27, 2026
Merged

Fix macOS filesystem security: use /etc/paths.d/TinyTeX when /usr/local/bin is not writable#489
yihui merged 16 commits intomainfrom
copilot/fix-filesystem-security-issues

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 6, 2026

TinyTeX was recursively changing ownership of /usr/local/bin (chown -R) — affecting all pre-existing binaries unrelated to TinyTeX — and creating symlinks from /usr/local/bin into the user's home directory. This PR eliminates unsafe /usr/local/bin manipulation on macOS, with a graceful fallback for users who have already made /usr/local/bin writable.

Changes

R/install.R

  • Remove check_local_bin() entirely
  • Add macos_local_bin_writable() helper (file.access('/usr/local/bin', 2) == 0)
  • Factor out macos_path(dir, action) helper used by both tlmgr_path() and use_tinytex(); skips writing /etc/paths.d/TinyTeX if it already contains the desired path (idempotent)
  • osascript(): emits message() before running to inform users of the command requiring the password; on failure emits warning() without repeating the command; escapes double-quotes in the shell command internally (keeping the original cmd for the message)
  • macos_path(): paths passed to shell commands are quoted to handle paths containing spaces; temp file is only deleted if osascript succeeds
  • use_tinytex(): uses macos_path() only when is_macos() && !macos_local_bin_writable(); warns on failure
  • add_path parameter doc simplified to link to tlmgr_path()

R/tlmgr.R

  • Factor out find_tinytex_bin() helper to avoid duplicating tweak_path() logic
  • tlmgr_path(): on macOS, uses macos_path() (writing to /etc/paths.d/TinyTeX) only when /usr/local/bin is not writable; if /usr/local/bin is writable, falls back to the existing tlmgr path add/remove behavior (preserving existing users' setups); on Linux/Windows the existing behavior is unchanged

tools/install-bin-unix.sh

  • On macOS: if /usr/local/bin is writable, use ./tlmgr path add; otherwise write the current bin directory to /etc/paths.d/TinyTeX via sudo tee (skipped if already up-to-date)
  • Supports a --no-path argument to skip PATH manipulation for any OS (detected by scanning all arguments, not assuming a fixed position)
  • On sudo failure: echoes a manual export PATH=$PATH:<bindir> command with instructions to add it to the shell startup profile; also runs the export immediately so the current shell session picks up the path without a restart
  • On Linux: unchanged ./tlmgr path add

tools/install-unx.sh

  • Unchanged from upstream (this script is primarily used for building binary TinyTeX in GHA, not for end-user macOS installs)
# Before (R/install.R)
osascript(sprintf('chown -R %s:admin %s', user, p))  # recursive chown on /usr/local/bin
# + tlmgr path add  (creates symlinks in /usr/local/bin -> ~/Library/TinyTeX/bin/*)

# After (R/tlmgr.R - tlmgr_path on macOS, /usr/local/bin not writable)
macos_path(find_tinytex_bin(), action)  # writes bin path to /etc/paths.d/TinyTeX; no /usr/local/bin involved

# After (R/tlmgr.R - tlmgr_path on macOS, /usr/local/bin is writable)
tlmgr(c('path', action))  # existing behavior preserved for users who already chmod'd /usr/local/bin
Original prompt

This section details on the original issue you should resolve

<issue_title>fix filesystem security issues</issue_title>
<issue_description>From #24 (comment):

/usr/local/bin should be writable by default on (at least recent versions of) macOS.

Please no 🤦 . No R package should ever automatically/directly change permissions on a file/directory outside of the user's home directory1, nor encourage it as a long-term solution. Further, your script is changing ownership for files that have nothing at all to do with TinyTex. Lastly, setting up symlinks from /usr/local/bin/ into any user's /Users/*/bin/ is as bad if not worse than changing the ownership of /usr/local/bin/; even if that one user keeps it up to date, it is bad practice and has risks.

There are two ways to do it "responsibly" (from an OS security mindset):

  1. Setup a Homebrew formula so that it can be installed and managed in a central location /opt/homebrew/..., usable to all users, and does not allow users to access other users' home directory contents. The presumption here is that the setup of Homebrew is permitted by a local user, and if on a multi-user system, who can do what with brew is for them to decide (not an R package maintainer ... no offense intended).
    I confess that I don't know homebrew well enough to be able to help much with this approach.
  2. Initially install completely to a local or temporary location, then use the ocascript "with administrator privileges" trick for moving everything to /usr/local/bin/ and /usr/local/share/TinyTex/, no symlinks required. This supports multi-users just as natively without any fearsome user-directory-breach nor any permanent side-effects such as from chown.

I might be willing to help with number 2, though I apologize it will not happen any time soon. But I think that's a much preferred way-forward than the current method.

As a temporary step, can you do one or more of the following:

  1. Add what the original owner/group were changed from? On my system I think it's root:staff but I can't know now. This allows security-obsessed people like me to undo the change to what it was before.
  2. There should be no need to recursively change all non-TinyTex contents of /usr/local/bin/ to be owned by me, please remove the -R from chown -R. (This should never have been needed in the first place, this removal should have zero effect on the installation.)
  3. Once installation is done, undo the change (using ocascript a second time) on /usr/local/bin/ (again, no need for recursive changes).
1: When external non-R software is required, if *that* software has a script that handles multi-user situations, then that is IMO an acceptable solution. No software I've ever installed on linux ever attempted to change ownership of `/usr/local/bin`, for many good reasons. Now that I've moved to MacOS, I continue the unix-y security mindsets, most of which Apple agrees with.

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 6, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ yihui
❌ Copilot
You have signed the CLA already but the status is still pending? Let us recheck it.

…l/bin ownership after install

Co-authored-by: yihui <163582+yihui@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix filesystem security issues in TinyTex installation Fix macOS /usr/local/bin ownership: drop -R, restore after install Mar 6, 2026
Copilot finished work on behalf of yihui March 6, 2026 21:57
Copy link
Copy Markdown
Contributor

@yihui yihui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of restoring ownership, let's use a different approach to solve the problem:

we don't tamper with /usr/local/bin at all, but use sudo only once to add TinyTeX's bin path to /etc/paths.d, e.g.

sudo echo path/to/TinyTeX/bin/* > /etc/paths.d/TinyTeX

then we should avoid tlmgr path add for macOS in all places; this command is called in several places in this package, some are through the R function tinytex::tlmgr_path() and some are tlmgr path add commands directly; please find out all of them and avoid them for macOS

the osascript() call in R should only do this /etc/paths.d operation and nothing else; i think we can get rid of check_local_bin() entirely since /usr/local/bin is no longer relevant

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 27, 2026

instead of restoring ownership, let's use a different approach to solve the problem:

we don't tamper with /usr/local/bin at all, but use sudo only once to add TinyTeX's bin path to /etc/paths.d, e.g....

Done in eea0101.

The approach now:

  • No /usr/local/bin changes at allcheck_local_bin() is removed entirely
  • On macOS, tlmgr_path('add') writes the TinyTeX bin dir path to /etc/paths.d/TinyTeX via osascript/sudo tee; tlmgr_path('remove') deletes that file
  • On Linux/non-macOS, the existing tlmgr path add/remove behavior is unchanged
  • All shell scripts (install-bin-unix.sh, install-unx.sh) have the same macOS/non-macOS split
  • use_tinytex() on macOS also uses the /etc/paths.d approach instead of tlmgr path add
  • osascript() is now only used for the /etc/paths.d operations

Copilot AI changed the title Fix macOS /usr/local/bin ownership: drop -R, restore after install Fix macOS filesystem security: use /etc/paths.d/TinyTeX instead of touching /usr/local/bin Mar 27, 2026
Copilot AI requested a review from yihui March 27, 2026 06:40
Copy link
Copy Markdown
Contributor

@yihui yihui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remember to roxygenize after doc changes

R/install.R Outdated
Comment on lines +36 to +39
#' @param add_path Whether to add the bin path of TeX Live to the system
#' environment variable \var{PATH}. On macOS, this writes the bin path to
#' \file{/etc/paths.d/TinyTeX}; on Linux, it runs \command{tlmgr path add};
#' on Windows, it runs \command{tlmgr path add}.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

document the details in tlmgr_path and don't repeat in other places

Suggested change
#' @param add_path Whether to add the bin path of TeX Live to the system
#' environment variable \var{PATH}. On macOS, this writes the bin path to
#' \file{/etc/paths.d/TinyTeX}; on Linux, it runs \command{tlmgr path add};
#' on Windows, it runs \command{tlmgr path add}.
#' @param add_path Whether to add the bin path of TeX Live to the system
#' environment variable \var{PATH}. See \code{\link{tlmgr_path}()}.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 76970e1.

#' \code{use_tinytex()} adds the copy of TinyTeX in an existing folder to the
#' \code{PATH} variable of the current system (on macOS via
#' \file{/etc/paths.d/TinyTeX}; on other platforms via \command{tlmgr path add}),
#' so that you can use utilities such as \command{tlmgr} and \command{pdflatex},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't mention OS details; link to tlmgr_path documentation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 76970e1.

R/tlmgr.R Outdated
Comment on lines +271 to +282
paths_file = '/etc/paths.d/TinyTeX'
if (action == 'add') {
f = getOption('tinytex.tlmgr.path', find_tlmgr(extra = TRUE))
if (length(f) == 0 || !file_test('-x', f)) return(invisible(1L))
bin = normalizePath(dirname(f))
tmp = tempfile(tmpdir = '/tmp')
writeLines(bin, tmp)
osascript(sprintf('cp %s %s', tmp, paths_file))
unlink(tmp)
} else {
osascript(sprintf('rm -f %s', paths_file))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

factor out a function macos_path(dir, add = TRUE) so it can be reused both here and in use_tinytex(), where dir is the bin path; this function should check if /etc/paths.d/TinyTeX already exists and contains the desired bin path; if it does, we should skip the creation of this file; the same check should be done in *.sh scripts, too

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 76970e1macos_path(dir, add) is factored out and used by both tlmgr_path() and use_tinytex(). It checks grep -qxF in the shell scripts and readLines + identical in R to skip the write when the file already contains the desired path.

R/tlmgr.R Outdated
Comment on lines +273 to +275
f = getOption('tinytex.tlmgr.path', find_tlmgr(extra = TRUE))
if (length(f) == 0 || !file_test('-x', f)) return(invisible(1L))
bin = normalizePath(dirname(f))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is repeating the code in tweak_path(); please always factor out, never copy

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 76970e1 — factored out as find_tinytex_bin() which both tweak_path() and tlmgr_path() (via macos_path()) now use.

fi
# add TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin
# (at this point we are in $TEXDIR/bin/*/ after the `cd` above)
TINYTEX_BINDIR="$(pwd)"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this var is not used elsewhere; inline it into printf call next

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 76970e1.

R/tlmgr.R Outdated
} else {
osascript(sprintf('rm -f %s', paths_file))
}
return(invisible(0L))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return status code of osascript()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 76970e1osascript() now captures and returns the exit status, and macos_path() propagates it via invisible(osascript(...)). tlmgr_path() returns invisible(macos_path(...)) on macOS.

R/tlmgr.R Outdated
f = getOption('tinytex.tlmgr.path', find_tlmgr(extra = TRUE))
if (length(f) == 0 || !file_test('-x', f)) return(invisible(1L))
bin = normalizePath(dirname(f))
tmp = tempfile(tmpdir = '/tmp')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think there's any reason to specify a hardcoded tmpdir

Suggested change
tmp = tempfile(tmpdir = '/tmp')
tmp = tempfile()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 76970e1.

…ncy, osascript returns status, roxygenize

Agent-Logs-Url: https://github.com/rstudio/tinytex/sessions/3a5f54ec-a104-4514-843a-93ea370973bf

Co-authored-by: yihui <163582+yihui@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 27, 2026

remember to roxygenize after doc changes

Done in 76970e1 — man pages updated to match the doc changes.

Copilot AI requested a review from yihui March 27, 2026 15:00
Copy link
Copy Markdown
Contributor

@yihui yihui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert changes in tools/install-unx.sh; it should be rare that any macOS user would run this script; the script is mainly used for building binary TinyTeX in our GHA

in tools/install-bin-unix.sh, is it possible to add support for a --no-path argument? if this flag is given, skip the PATH manipulation

# (at this point we are in $TEXDIR/bin/*/ after the `cd` above)
if ! grep -qxF "$(pwd)" /etc/paths.d/TinyTeX 2>/dev/null; then
echo "Admin privilege (password) is required to set up the PATH for TinyTeX:"
printf '%s\n' "$(pwd)" | sudo tee /etc/paths.d/TinyTeX > /dev/null
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this fails (e.g. user gave up password input), echo a message telling users how to set up PATH; the command would be something like export PATH=$PATH:$(pwd) (note that in the message, $PATH shouldn't be expanded and $(pwd) should), and this command should be added to their shell startup profile

also run this export command right here, because the /etc/paths.d won't be picked up until the shell is restarted

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 14a151f — on sudo failure, we echo the manual export PATH=$PATH:$(pwd) command with instructions to add it to the shell startup profile. In both success and failure cases, export PATH="$PATH:$(pwd)" is run immediately so the current shell picks it up without a restart.

R/install.R Outdated

# add/remove TinyTeX's bin path to/from /etc/paths.d/TinyTeX on macOS;
# if adding and the file already contains the desired path, skip the operation
macos_path = function(dir = NULL, add = TRUE) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the argument action to be consistent with tlmgr_path():

Suggested change
macos_path = function(dir = NULL, add = TRUE) {
macos_path = function(dir = NULL, action = 'add') {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 14a151f.

R/install.R Outdated
"Please run this command in your Terminal (password required):\n sudo ",
cmd, call. = FALSE
)
invisible(ret)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
invisible(ret)
ret

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 14a151f.

R/install.R Outdated
Comment on lines +642 to +644
if (is_macos()) {
macos_path(normalizePath(d))
} else if (system2(p, c('path', 'add')) != 0) stop(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for macOS, we also need to stop() when exit status is not 0

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 14a151f.

R/install.R Outdated
Comment on lines +307 to +318
if (add) {
if (is.null(dir) || dir == '') return(invisible(1L))
if (file.exists(paths_file) &&
identical(trimws(readLines(paths_file, warn = FALSE)), dir))
return(invisible(0L))
tmp = tempfile()
on.exit(unlink(tmp), add = TRUE)
writeLines(dir, tmp)
invisible(osascript(sprintf('cp %s %s', tmp, paths_file)))
} else {
invisible(osascript(sprintf('rm -f %s', paths_file)))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (add) {
if (is.null(dir) || dir == '') return(invisible(1L))
if (file.exists(paths_file) &&
identical(trimws(readLines(paths_file, warn = FALSE)), dir))
return(invisible(0L))
tmp = tempfile()
on.exit(unlink(tmp), add = TRUE)
writeLines(dir, tmp)
invisible(osascript(sprintf('cp %s %s', tmp, paths_file)))
} else {
invisible(osascript(sprintf('rm -f %s', paths_file)))
}
cmd = if (add) {
if (is.null(dir) || dir == '') return(1L)
if (file.exists(paths_file) &&
identical(readLines(paths_file, warn = FALSE), dir))
return(0L)
tmp = tempfile()
on.exit(unlink(tmp), add = TRUE)
writeLines(dir, tmp)
sprintf('cp %s %s', tmp, paths_file))
} else {
sprintf('rm -f %s', paths_file)
}
osascript(cmd)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 14a151f.

R/tlmgr.R Outdated
Comment on lines +274 to +275
if (is_macos()) return(invisible(macos_path(find_tinytex_bin(), action == 'add')))
tlmgr(c('path', action), .quiet = TRUE)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (is_macos()) return(invisible(macos_path(find_tinytex_bin(), action == 'add')))
tlmgr(c('path', action), .quiet = TRUE)
if (is_macos()) {
invisible(macos_path(find_tinytex_bin(), action == 'add'))
} else tlmgr(c('path', action), .quiet = TRUE)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 14a151f.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 27, 2026

revert changes in tools/install-unx.sh; it should be rare that any macOS user would run this script; the script is mainly used for building binary TinyTeX in our GHA

in tools/install-bin-unix.sh, i...

Done in 14a151ftools/install-unx.sh is reverted to the original. tools/install-bin-unix.sh now accepts a --no-path argument to skip PATH manipulation.

Copilot AI requested a review from yihui March 27, 2026 17:39
sudo chown -R `whoami`:admin /usr/local/bin
# add TinyTeX's bin path to /etc/paths.d instead of creating symlinks in /usr/local/bin
# (at this point we are in $TEXDIR/bin/*/ after the `cd` above)
if [ "$1" != '--no-path' ] && ! grep -qxF "$(pwd)" /etc/paths.d/TinyTeX 2>/dev/null; then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two issues:

  1. the --no-path argument is not only for macOS, so raise the condition to the top; when --no-path is present, just skip path manipulation for any OS
  2. is there a way to way to test the presence of --no-path without assuming it's $1?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 67441ce. The --no-path detection now scans all arguments via a for arg in "$@" loop and sets a NO_PATH flag. The entire path manipulation block (both Darwin and Linux branches) is wrapped under if [ $NO_PATH -eq 0 ], so the flag applies to any OS.

R/install.R Outdated
Comment on lines 644 to 652
if (is_macos()) {
if (macos_path(normalizePath(d)) != 0) stop(
"Failed to add '", d, "' to /etc/paths.d/TinyTeX. You may consider the fallback ",
"approach, i.e., set options(tinytex.tlmgr.path = '", p, "')."
)
} else if (system2(p, c('path', 'add')) != 0) stop(
"Failed to add '", d, "' to your system's environment variable PATH. You may ",
"consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')."
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too much repetition in the two messages; consolidate into one stop() call; it's fine to use the second message for the macOS case, even though the description is not totally accurate

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 67441ce — both branches now assign to ret and share a single stop() call using the second message.

R/install.R Outdated
Comment on lines +316 to +318
sprintf('cp %s %s', tmp, paths_file)
} else {
sprintf('rm -f %s', paths_file)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do these paths need to be quoted somehow? what if they contain spaces or literal quotes? would osascript fail in that case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 67441ce. Paths are now wrapped with \" (backslash-escaped double quotes) in the shell command string: sprintf('cp \\"%s\\" \\"%s\\"', tmp, paths_file). This produces a command like cp \"/path/with spaces\" \"/etc/paths.d/TinyTeX\", which AppleScript's do shell script correctly interprets as double-quoted shell arguments, handling spaces in paths. Literal double-quotes in paths (extremely unusual for file paths) are not handled, but that edge case is not a practical concern.

R/install.R Outdated
identical(readLines(paths_file, warn = FALSE), dir))
return(0L)
tmp = tempfile()
on.exit(unlink(tmp), add = TRUE)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't unlink tmp on exit because the osascript command may fail, in which case we should keep tmp, otherwise the warning() message in osascript() will point users to a nonexistent file to cp/rm

unlink tmp only if osascript succeeds

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 67441ce. Removed the on.exit(unlink(tmp)) and replaced it with if (add && ret == 0) unlink(tmp) after the osascript() call. The temp file persists on failure, so the sudo cp ... path in the warning message remains valid for the user to run manually.

…k only on success, consolidate stop()

Agent-Logs-Url: https://github.com/rstudio/tinytex/sessions/27d65823-31ba-4ff1-ba16-17c9c18bd877

Co-authored-by: yihui <163582+yihui@users.noreply.github.com>
Copilot AI requested a review from yihui March 27, 2026 18:03
Copy link
Copy Markdown
Contributor

@yihui yihui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in R code, the condition to run macos_path() is currently is_macos(); i want to add another condition: test if /usr/local/bin is writable, if it is, follow the old behavior (i.e., run tlmgr path add); if not, use the new macos_path(); remember to update doc for tlmgr_path() accordingly and roxygenize

same thing for .sh: check if /usr/local/bin is writable

existing TinyTeX users may have already chmod /usr/local/bin, and i don't want to bother them with another sudo command to add /etc/paths.d/TinyTeX

Comment on lines +293 to 299
ret = system(sprintf(
"/usr/bin/osascript -e 'do shell script \"%s\" with administrator privileges'", cmd
)) != 0) warning(
))
if (ret != 0) warning(
"Please run this command in your Terminal (password required):\n sudo ",
cmd, call. = FALSE
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before running system() command, emit a message() telling users the command to be executed so they know the purpose of requiring the password; after running system(), if it fails, add warning() to tell you to run the command in terminal (don't repeat the command in the warning message since it has been mentioned in message() before)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 71d49e3. osascript() now emits message("Requesting admin privilege to run: sudo ", cmd) before the system() call, and on failure emits warning("Please run the above command in your Terminal (password required).") without repeating the command.

R/install.R Outdated
Comment on lines +315 to +317
sprintf('cp \\"%s\\" \\"%s\\"', tmp, paths_file)
} else {
sprintf('rm -f \\"%s\\"', paths_file)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't escape quotes here, but do it in osascript() instead; however, in osascript(), you should keep the original cmd instead of overriding it with the escaped version, because in message() you need to output the original cmd, but for /usr/bin/osascript you need to pass the escaped version

otherwise in message() users would see escaped quotes, and the cmd wouldn't be valid for them to run

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 71d49e3. macos_path() now uses plain sprintf('cp "%s" "%s"', ...) (no escaping). In osascript(), a local escaped = gsub('"', '\\"', cmd, fixed = TRUE) is used for the shell command passed to /usr/bin/osascript, while the original cmd is used in message() so users see clean, runnable commands.

@yihui yihui marked this pull request as ready for review March 27, 2026 18:36
…ipt(); move quote escaping to osascript()

Agent-Logs-Url: https://github.com/rstudio/tinytex/sessions/1bca78ef-48fe-4a0f-bf5c-3be15fdc5da8

Co-authored-by: yihui <163582+yihui@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 27, 2026

in R code, the condition to run macos_path() is currently is_macos(); i want to add another condition: test if /usr/local/bin is writable, if it is, follow the old behavior (i.e., run tlmgr path add...

Done in 71d49e3. Added macos_local_bin_writable() helper (file.access('/usr/local/bin', 2) == 0). Both tlmgr_path() and use_tinytex() now use macos_path() only when is_macos() && !macos_local_bin_writable(), falling back to tlmgr path add when /usr/local/bin is writable. The .sh script on Darwin now checks [ -w /usr/local/bin ] first and runs ./tlmgr path add if true, otherwise falls through to the /etc/paths.d/TinyTeX approach. Doc and Rd updated accordingly.

Copilot AI changed the title Fix macOS filesystem security: use /etc/paths.d/TinyTeX instead of touching /usr/local/bin Fix macOS filesystem security: use /etc/paths.d/TinyTeX when /usr/local/bin is not writable Mar 27, 2026
Copilot AI requested a review from yihui March 27, 2026 18:42
@yihui yihui merged commit 68dbf58 into main Mar 27, 2026
12 of 13 checks passed
@yihui yihui deleted the copilot/fix-filesystem-security-issues branch March 27, 2026 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix filesystem security issues

3 participants